/*
    Wild Flamenco metronome 0.5
    -
    By David Reguera Garcia - Dreg@fr33project.org -
    http://wildflamenco.blogspot.com/

    Copyright (C) <2011>  <David Reguera Garcia>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

*/

#include <windows.h>
#include <stdio.h>
#include "resource.h"
#include "patterns.h"


#define MAX_BPM_ALLOWED 600
#define DEFAULT_BPM_STR "180"
#define DEFAULT_BPM atoi( DEFAULT_BPM_STR )
#define DEFAULT_COMPAS_STR "1"
#define DEFAULT_COMPAS atoi( DEFAULT_COMPAS_STR )
#define MAX_COMPAS_ALLOWED 200

#define ConvertBPMToMS( bpm ) ( (double) 60000 / bpm )

LPSTR golpe; 
LPSTR silencio;
LPSTR palma;

void CALLBACK TimeProc( UINT uID, UINT uMsg, DWORD input, DWORD dw1, DWORD dw2 )
{     
    CONTAINER_t * pattern;

    pattern = (CONTAINER_t *) input;

    PlaySoundA
    ( 
        ** ( (LPSTR **) ContainerGetElement(  pattern ) ), 
        NULL, 
        SND_MEMORY | SND_SYNC | SND_NODEFAULT 
    );

    ContainerNextElement( pattern, NULL );
}

void StartStopTimer( BOOL start, CONTAINER_t * pattern, unsigned int bpm )
{
    double ms;
    TIMECAPS tc;
    static MMRESULT timer_id = 0;
    static UINT resolution = 0;

    if ( timer_id )
        timeKillEvent( timer_id );
    
    if ( resolution )
        timeEndPeriod( resolution );

    if ( start )
    {
        ms = ConvertBPMToMS( bpm );
    
        timeGetDevCaps( & tc, sizeof( tc ) );
        resolution = tc.wPeriodMin;
    
        timeBeginPeriod( resolution );
    
        ContainerResetIndex( pattern );

        Sleep( 400 );
        timer_id = timeSetEvent( ms, resolution, TimeProc, (DWORD) pattern, TIME_PERIODIC );
    }
}

void FixBPM( HWND hDlg, unsigned int * bpm )
{
    SetDlgItemTextA(hDlg,IDC_EDIT1,DEFAULT_BPM_STR);
    * bpm = DEFAULT_BPM;
}

BOOL GetTheResource(LPSTR lpName, LPSTR * lpRes ) 
{ 
    BOOL bRtn; 
    HANDLE hResInfo, hRes; 
    HINSTANCE hInst;

    hInst = GetModuleHandle( NULL );

    // Find the WAVE resource. 
    hResInfo = FindResourceA(hInst, lpName, "WAVE"); 
    if (hResInfo == NULL) 
        return FALSE; 

    // Load the WAVE resource. 
 
    hRes = LoadResource(hInst, hResInfo); 
    if (hRes == NULL) 
        return FALSE; 
 
    // Lock the WAVE resource and play it. 
 
    * lpRes = LockResource(hRes); 
    if (* lpRes != NULL) { 

//        UnlockResource(hRes); 
    } 
    else 
        bRtn = 0; 
 
    // Free the WAVE resource and return success or failure. 
 
   // FreeResource(hRes); 
    return bRtn; 
} 

/*
typedef enum flag_palos_e { 
    BULERIA_FLAG,
    BULERIA_FLAG_2,
    SOLEA_FLAG,
    SIGUIRIYAS_FLAG,
    ALEGRIAS_FLAG,
    FANDANGOS_FLAG
} flag_palos_t;

void CreatePattern12( PATTERN_t * pattern, int flag )
{
    int i;
    i = 0;

    if ( pattern->resource_array != NULL )
        free( pattern->resource_array );

    switch ( flag )
    {
        case FANDANGOS_FLAG:
        case BULERIA_FLAG:
            pattern->nr_ct.ct = 0;
        break;

        case ALEGRIAS_FLAG:
        case SOLEA_FLAG:
            pattern->nr_ct.ct = 1;
        break;

        case SIGUIRIYAS_FLAG:
            pattern->nr_ct.ct = 6;
        break;
    }
           
    pattern->nr_ct.nr_elements = 12;
    pattern->resource_array = calloc( 1, sizeof( LPSTR ) * 12 );

    if ( flag == BULERIA_FLAG_2 )
    {
        pattern->resource_array[i++] = palma;
        pattern->resource_array[i++] = palma;
        pattern->resource_array[i++] = NULL;

        pattern->resource_array[i++] = palma;
        pattern->resource_array[i++] = palma;
        pattern->resource_array[i++] = NULL;

        pattern->resource_array[i++] = palma;
        pattern->resource_array[i++] = palma;
        pattern->resource_array[i++] = NULL;

        pattern->resource_array[i++] = palma;
        pattern->resource_array[i++] = palma;
        pattern->resource_array[i++] = NULL;
    }
    
    else
    {
        pattern->resource_array[i++] = golpe;
        pattern->resource_array[i++] = palma;
        pattern->resource_array[i++] = palma;
    
        pattern->resource_array[i++] = golpe;
        pattern->resource_array[i++] = palma;
        pattern->resource_array[i++] = palma;
    
        if ( flag == FANDANGOS_FLAG )
        {
            pattern->resource_array[i++] = golpe;
            pattern->resource_array[i++] = palma;
            pattern->resource_array[i++] = palma;
        
            pattern->resource_array[i++] = golpe;
            pattern->resource_array[i++] = palma;
            pattern->resource_array[i++] = palma;
        }
        else
        {
    
            pattern->resource_array[i++] = golpe;
            pattern->resource_array[i++] = palma;
    
            pattern->resource_array[i++] = golpe;
            pattern->resource_array[i++] = palma;
    
            pattern->resource_array[i++] = golpe;
            pattern->resource_array[i++] = palma;
        }
    }
}

void CreatePatternTangoRumba( PATTERN_t * pattern )
{
    int i;
    i = 0;

    if ( pattern->resource_array != NULL )
        free( pattern->resource_array );

    pattern->nr_ct.ct = 0;
        
    pattern->nr_ct.nr_elements = 4;
    pattern->resource_array = calloc( 1, sizeof( LPSTR ) * 4 );

    pattern->resource_array[i++] = NULL;
    pattern->resource_array[i++] = palma;
    pattern->resource_array[i++] = palma;
    pattern->resource_array[i++] = palma;
}

typedef struct NAME_SOUND_s
{
    char * name;
    LPSTR lpstr;
} NAME_SOUND_t;

typedef struct DIALOG2_ARGS_s
{
    char cancel;
    int now;
    int total;
} DIALOG2_ARGS_t; 

DIALOG2_ARGS_t dialog2_args;

BOOL CALLBACK AppDlgProc2(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    int i, j;
    HWND hwndList;
    int lbItem;
    int idcs [] = 
    {
        IDC_LIST1, 
        IDC_LIST2, 
        IDC_LIST3,
        IDC_LIST4, 
        IDC_LIST5, 
        IDC_LIST6, 
        IDC_LIST7, 
        IDC_LIST8, 
        IDC_LIST9, 
        IDC_LIST10, 
        IDC_LIST11, 
        IDC_LIST12
    };
    NAME_SOUND_t builds_noise[] = 
     { 
        {"golpe", golpe },
        {"palma", palma }, 
        { "silencio", NULL } 
    };
    char numberof[100];

    memset( numberof, 0, sizeof( numberof ) );


switch(uMsg)
  {
    
  case WM_INITDIALOG:
    sprintf( numberof, "%d of %d", dialog2_args.now, dialog2_args.total ) ;
    SetDlgItemTextA(hDlg,IDC_EDIT2, numberof);
    for ( j = 0; j < ( sizeof( idcs ) / sizeof( * idcs ) ); j++ )
    {
        hwndList = GetDlgItem(hDlg, idcs[j] );  
        for ( i = 0; i < ( sizeof(builds_noise) / sizeof( * builds_noise ) ); i++) 
        { 
            int pos = (int)SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM) builds_noise[i].name); 
            SendMessage(hwndList, LB_SETITEMDATA, pos, (LPARAM) i); 
        }
        SendMessage(hwndList, LB_SETCURSEL, 0, 0); 
    }

    return 1;

  case WM_COMMAND:
    switch(wParam)
    {

    case IDCANCEL:
        if ( timer_id )
            timeKillEvent( timer_id );
        
        if ( resolution )
            timeEndPeriod( resolution );

        dialog2_args.cancel = 1;

      EndDialog(hDlg, 0);
    break;

    case IDC_BUTTON2:
        patterns.patterns[dialog2_args.now - 1].nr_ct.nr_elements = 12;
        patterns.patterns[dialog2_args.now - 1].nr_ct.ct = 0;
        patterns.patterns[dialog2_args.now - 1].resource_array = calloc( 1, sizeof( LPSTR ) * 12 );

        for ( j = 0; j < ( sizeof( idcs ) / sizeof( * idcs ) ); j++ )
        {
            hwndList = GetDlgItem(hDlg, idcs[j]); 
            lbItem = (int)SendMessage(hwndList, LB_GETCURSEL, 0, 0); 
            i = (int)SendMessage(hwndList, LB_GETITEMDATA, lbItem, 0);
            patterns.patterns[dialog2_args.now - 1].resource_array[j] = builds_noise[i].lpstr;
        }

        if ( timer_id )
            timeKillEvent( timer_id );
        
        if ( resolution )
            timeEndPeriod( resolution );

        ms = ConvertBPMToMS( DEFAULT_BPM );
    
        timeGetDevCaps( & tc, sizeof( tc ) );
        resolution = tc.wPeriodMin;
    
        timeBeginPeriod( resolution );
    
        Sleep( 0 );
        timer_id = timeSetEvent( ms, resolution, TimeProc, 0, TIME_PERIODIC );

    break;

    case IDC_BUTTON3:
        if ( timer_id )
            timeKillEvent( timer_id );
        
        if ( resolution )
            timeEndPeriod( resolution );
    break;

    case IDOK:
        if ( timer_id )
            timeKillEvent( timer_id );
        
        if ( resolution )
            timeEndPeriod( resolution );

        patterns.patterns[dialog2_args.now - 1].nr_ct.nr_elements = 12;
        patterns.patterns[dialog2_args.now - 1].nr_ct.ct = 0;
        patterns.patterns[dialog2_args.now - 1].resource_array = calloc( 1, sizeof( LPSTR ) * 12 );

        for ( j = 0; j < ( sizeof( idcs ) / sizeof( * idcs ) ); j++ )
        {
            hwndList = GetDlgItem(hDlg, idcs[j]); 
            lbItem = (int)SendMessage(hwndList, LB_GETCURSEL, 0, 0); 
            i = (int)SendMessage(hwndList, LB_GETITEMDATA, lbItem, 0);
            patterns.patterns[dialog2_args.now - 1].resource_array[j] = builds_noise[i].lpstr;
        }

      EndDialog(hDlg, 0);
    break;
    }
  }
  return 0;
}

BOOL CALLBACK AppDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    char bpm_str[100];
    unsigned int bpm;
    double ms;
    TIMECAPS tc;
    int i;
    HWND hwndList;
    int pos;
    char nr_compas_str[100];
    unsigned int nr_compas;
    NEW_PATTERNS_t new_pattern_aux;

    memset( bpm_str, 0, sizeof( bpm_str ) );

  switch(uMsg)
  {
  case WM_INITDIALOG:
        GetTheResource( "golpe", & golpe  );
        GetTheResource( "silencio", & silencio  );
        GetTheResource( "palma", & palma  );
    
        SetClassLongPtr(hDlg, GCLP_HICON, (long)LoadIcon( GetModuleHandle( NULL ), MAKEINTRESOURCE(IDI_ICON1)));
        SetDlgItemTextA(hDlg,IDC_EDIT1,DEFAULT_BPM_STR);
        SetDlgItemTextA(hDlg,IDC_EDIT3,DEFAULT_COMPAS_STR);
        SendMessage(GetDlgItem(hDlg, IDC_RADIO1), BM_SETCHECK, TRUE, 0);
    
        hwndList = GetDlgItem(hDlg, IDC_LIST1);  
        for ( i = 0; i < 2; i++) 
        { 
            pos = (int)SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM) "AA"); 
        }
        SendMessage(hwndList, LB_SETCURSEL, 0, 0); 

    return 1;
  case WM_COMMAND:
    switch(wParam)
    {
    case IDC_BUTTON1:
        ShellExecute(NULL, "open", "http://wildflamenco.blogspot.com/", NULL, NULL, SW_SHOWNORMAL);
    break;
    case IDSTARTT:
        if ( timer_id )
            timeKillEvent( timer_id );
        
        if ( resolution )
        {
            timeEndPeriod( resolution );
            Sleep( 400 );
        }

        if ( GetWindowTextA( GetDlgItem(hDlg, IDC_EDIT1), bpm_str, sizeof(bpm_str) - 1) == 0 )
            FixBPM( hDlg, & bpm );
        else
        {
            bpm = atoi( bpm_str );
            if ( bpm >= MAX_BPM_ALLOWED )
                FixBPM( hDlg, & bpm );
        }

        if(SendMessage(GetDlgItem(hDlg,IDC_RADIO8), BM_GETCHECK, (WPARAM)0 ,(LPARAM)0) == BST_CHECKED)
        {
            patterns.patterns = calloc( 1, sizeof(* patterns.patterns) * 4 );
            patterns.nr_ct.ct = 0;
            patterns.nr_ct.nr_elements = 4;

            i = 0;

            CreatePattern12( & patterns.patterns[i++], BULERIA_FLAG_2 );
            CreatePattern12( & patterns.patterns[i++], BULERIA_FLAG_2 );
            CreatePattern12( & patterns.patterns[i++], BULERIA_FLAG );
            CreatePattern12( & patterns.patterns[i++], BULERIA_FLAG );
        }
        else
        {
            patterns.patterns = calloc( 1, sizeof(* patterns.patterns) * 1 );
            patterns.nr_ct.ct = 0;
            patterns.nr_ct.nr_elements = 1;

            if(SendMessage(GetDlgItem(hDlg,IDC_RADIO1), BM_GETCHECK, (WPARAM)0 ,(LPARAM)0) == BST_CHECKED)
                 CreatePattern12( & patterns.patterns[0], BULERIA_FLAG );

            else if(SendMessage(GetDlgItem(hDlg,IDC_RADIO2), BM_GETCHECK, (WPARAM)0 ,(LPARAM)0) == BST_CHECKED)
                CreatePattern12( & patterns.patterns[0], SOLEA_FLAG );
    
            else if(SendMessage(GetDlgItem(hDlg,IDC_RADIO3), BM_GETCHECK, (WPARAM)0 ,(LPARAM)0) == BST_CHECKED || SendMessage(GetDlgItem(hDlg,IDC_RADIO7), BM_GETCHECK, (WPARAM)0 ,(LPARAM)0) == BST_CHECKED )
                CreatePatternTangoRumba( & patterns.patterns[0] );
    
            else if( SendMessage(GetDlgItem(hDlg,IDC_RADIO5), BM_GETCHECK, (WPARAM)0 ,(LPARAM)0) == BST_CHECKED )
                CreatePattern12( & patterns.patterns[0], SIGUIRIYAS_FLAG );
    
            else if(SendMessage(GetDlgItem(hDlg,IDC_RADIO4), BM_GETCHECK, (WPARAM)0 ,(LPARAM)0) == BST_CHECKED)
                CreatePattern12( & patterns.patterns[0], ALEGRIAS_FLAG );
    
            else if(SendMessage(GetDlgItem(hDlg,IDC_RADIO6), BM_GETCHECK, (WPARAM)0 ,(LPARAM)0) == BST_CHECKED)
                CreatePattern12( & patterns.patterns[0], FANDANGOS_FLAG );

            else if(SendMessage(GetDlgItem(hDlg,IDC_RADIO9), BM_GETCHECK, (WPARAM)0 ,(LPARAM)0) == BST_CHECKED)
                memcpy( & patterns, & new_patterns.patterns[0].patterns, sizeof( patterns ) );
        }


        ms = ConvertBPMToMS( bpm );
    
        timeGetDevCaps( & tc, sizeof( tc ) );
        resolution = tc.wPeriodMin;
    
        timeBeginPeriod( resolution );
    
        Sleep( 0 );
        timer_id = timeSetEvent( ms, resolution, TimeProc, 0, TIME_PERIODIC );


    return 0;

    case IDC_BUTTON2:
        
        memset( nr_compas_str, 0, sizeof( nr_compas_str ) - 1 );
        GetWindowTextA( GetDlgItem(hDlg, IDC_EDIT3), nr_compas_str, sizeof(nr_compas_str) - 1);
        nr_compas = atoi( nr_compas_str );
        
        if ( nr_compas > MAX_COMPAS_ALLOWED )
        {
            MessageBoxA( hDlg, "Error: TOO COMPAS - MUCHOS COMPASES", "Error: TOO COMPAS - MUCHOS COMPASES", MB_OK );
            SetDlgItemTextA(hDlg,IDC_EDIT3,DEFAULT_COMPAS_STR);
        }
        else
        {      
            memset( & dialog2_args, 0, sizeof( dialog2_args ) );
 
            patterns.patterns = calloc( 1, sizeof(* patterns.patterns) * nr_compas );
            patterns.nr_ct.ct = 0;
            patterns.nr_ct.nr_elements = nr_compas;

            dialog2_args.total = nr_compas;
            for ( i = 0; i < nr_compas; i++ )
            {
                dialog2_args.now = i + 1;
                
                DialogBoxParam( NULL, MAKEINTRESOURCE( IDD_DIALOG2 ), hDlg, AppDlgProc2, 0 );
                if (  dialog2_args.cancel != 0 )
                    break;
            }
            if ( dialog2_args.cancel == 0 )
            {
                new_patterns.ct++;
                new_pattern_aux.patterns = calloc( 1, sizeof( * new_patterns.patterns ) * new_patterns.ct );
                new_pattern_aux.ct = new_patterns.ct;
                memcpy( new_pattern_aux.patterns, new_patterns.patterns, sizeof( * new_patterns.patterns ) * ( new_pattern_aux.ct - 1 ) );
                new_patterns.patterns = new_pattern_aux.patterns;

                itoa( current_rythm++, new_patterns.patterns[new_pattern_aux.ct - 1].name, sizeof( new_patterns.patterns[new_pattern_aux.ct - 1].name ) );
                new_patterns.patterns[new_pattern_aux.ct - 1].patterns = patterns;
            
                hwndList = GetDlgItem(hDlg, IDC_LIST1);  
                
                pos = (int)SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM) new_patterns.patterns[new_pattern_aux.ct - 1].name ); 
                
                SendMessage(hwndList, LB_SETCURSEL, 0, 0); 
            }
        }
    break;

    case IDSTOPP:
        if ( timer_id )
            timeKillEvent( timer_id );
        
        if ( resolution )
            timeEndPeriod( resolution );
    break;

    case IDCANCEL:
        srand(time(NULL));
        int r = rand() % 100;
        // I dont want a spam overflow x)
        if ( r < 3 )
            ShellExecute(NULL, "open", "http://wildflamenco.blogspot.com/", NULL, NULL, SW_SHOWNORMAL);


       if ( timer_id )
            timeKillEvent( timer_id );
        
       if ( resolution )
            timeEndPeriod( resolution );

      EndDialog(hDlg, 0);
    }
  }
  return 0;
}

*/

typedef struct NAME_SOUND_s
{
    char * name;
    LPSTR * lpstr;
} NAME_SOUND_t;

typedef struct DIALOG2_ARGS_s
{
    char cancel;
    int now;
    int total;
    CONTAINER_t pattern;

} DIALOG2_ARGS_t; 

DIALOG2_ARGS_t dialog2_args;


BOOL CALLBACK AppDlgProc2(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    int i, j;
    HWND hwndList;
    int lbItem;
    int idcs [] = 
    {
        IDC_LIST1, 
        IDC_LIST2, 
        IDC_LIST3,
        IDC_LIST4, 
        IDC_LIST5, 
        IDC_LIST6, 
        IDC_LIST7, 
        IDC_LIST8, 
        IDC_LIST9, 
        IDC_LIST10, 
        IDC_LIST11, 
        IDC_LIST12
    };
    NAME_SOUND_t builds_noise[] = 
     { 
        {"golpe", & golpe },
        {"palma", & palma }, 
        { "silencio", & silencio } 
    };
    char numberof[100];
    static LPSTR * pattern[12];

    memset( numberof, 0, sizeof( numberof ) );

    switch( uMsg )
    {
        case WM_INITDIALOG:
                StartStopTimer( FALSE, NULL, 0 );

                sprintf( numberof, "%d of %d", dialog2_args.now + 1, dialog2_args.total ) ;
                SetDlgItemTextA(hDlg,IDC_EDIT2, numberof);
                for ( j = 0; j < ( sizeof( idcs ) / sizeof( * idcs ) ); j++ )
                {
                    hwndList = GetDlgItem(hDlg, idcs[j] );  
                    for ( i = 0; i < ( sizeof(builds_noise) / sizeof( * builds_noise ) ); i++) 
                    { 
                        int pos = (int)SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM) builds_noise[i].name); 
                        SendMessage(hwndList, LB_SETITEMDATA, pos, (LPARAM) i); 
                    }
                    SendMessage(hwndList, LB_SETCURSEL, 0, 0); 
                }
        
                return 1;

        break;

        case WM_COMMAND:
            switch(wParam)
            {
                case IDCANCEL:
                    StartStopTimer( FALSE, NULL, 0 );

                    dialog2_args.cancel = TRUE;
            
                    EndDialog(hDlg, 0);
                break;

                case IDC_BUTTON3:
                    StartStopTimer( FALSE, NULL, 0 );
                break;

                case IDOK:
                case IDC_BUTTON2:
                    StartStopTimer( FALSE, NULL, 0 );

                    for ( j = 0; j < ( sizeof( idcs ) / sizeof( * idcs ) ); j++ )
                    {
                        hwndList = GetDlgItem(hDlg, idcs[j]); 
                        lbItem = (int)SendMessage(hwndList, LB_GETCURSEL, 0, 0); 
                        i = (int)SendMessage(hwndList, LB_GETITEMDATA, lbItem, 0);
                        pattern[j] = builds_noise[i].lpstr;
                    }

                    switch(wParam)
                    {
                        case IDOK:
                            ContainerInit
                            ( 
                                & dialog2_args.pattern, 
                                pattern, sizeof( pattern ), 
                                sizeof( * pattern ), 
                                TRUE
                            );  

                            dialog2_args.cancel == FALSE;
                            EndDialog(hDlg, 0);
                        break;

                        case IDC_BUTTON2:
                            ContainerInit
                            ( 
                                & dialog2_args.pattern, 
                                pattern, sizeof( pattern ), 
                                sizeof( * pattern ), 
                                FALSE 
                            ); 

                            StartStopTimer( TRUE, & dialog2_args.pattern, DEFAULT_BPM );
                        break;   
                    }
                    
                    
                break;
            }
        break;
    }

    return 0;
}

typedef struct ALL_RYTHMS_s
{
    char * name;
    LPSTR * pattern;
    unsigned int size;  
    int idc_dialogs;
 
} ALL_RYTHMS_t;

#define BuiltSoundEntry( name, pattern, idc ) name, pattern, sizeof( pattern ), idc 

void MakeBuiltSounds( void )
{
    int i;
    LPSTR * bulerias_pattern[] = {
        & golpe, & palma, & palma,
        & golpe, & palma, & palma,
        & golpe, & palma,
        & golpe, & palma,
        & golpe, & palma
    };
    LPSTR * fandangos_pattern[] = {
        & golpe, & palma, & palma,
        & golpe, & palma, & palma,
        & golpe, & palma, & palma,
        & golpe, & palma, & palma
    };
    LPSTR * solea_pattern[] = {
        & palma, & palma, & golpe, 
        & palma, & palma, & golpe, 
        & palma, & golpe, 
        & palma, & golpe,
        & palma, & golpe
    };
    LPSTR * alegrias_pattern[] = {
       & palma, & palma, & golpe, 
        & palma, & palma, & golpe, 
        & palma, & golpe, 
        & palma, & golpe,
        & palma, & golpe
    };
    LPSTR * siguiriyas_pattern[] = {
        & golpe, & palma,
        & golpe, & palma,
        & golpe, & palma, & palma,
        & golpe, & palma, & palma,
        & golpe, & palma,
    };
    LPSTR * tangos_pattern[] = {
        & silencio, & palma, & palma, & palma
    };
    LPSTR * rumba_pattern[] = {
        & silencio, & palma, & palma, & palma
    };
    LPSTR * bulerias4_pattern[] = {
        & silencio, & palma, & palma,
        & silencio, & palma, & palma,
        & silencio, & palma, & palma,
        & silencio, & palma, & palma,

        & silencio, & palma, & palma,
        & silencio, & palma, & palma,
        & silencio, & palma, & palma,
        & silencio, & palma, & palma,

        & golpe, & palma, & palma,
        & golpe, & palma, & palma,
        & golpe, & palma,
        & golpe, & palma,
        & golpe, & palma,

        & golpe, & palma, & palma,
        & golpe, & palma, & palma,
        & golpe, & palma,
        & golpe, & palma,
        & golpe, & palma
    };
    ALL_RYTHMS_t all_rythms[] =
    {
        { BuiltSoundEntry( "bulerias4", bulerias4_pattern, IDC_RADIO8 ) },
        { BuiltSoundEntry( "bulerias", bulerias_pattern, IDC_RADIO1 ) }, 
        { BuiltSoundEntry( "solea", solea_pattern, IDC_RADIO2 ) }, 
        { BuiltSoundEntry( "alegrias", alegrias_pattern, IDC_RADIO4 ) },
        { BuiltSoundEntry( "siguiriyas", siguiriyas_pattern, IDC_RADIO5 ) },  
        { BuiltSoundEntry( "tangos", tangos_pattern, IDC_RADIO3 ) }, 
        { BuiltSoundEntry( "rumba", rumba_pattern, IDC_RADIO7 ) }, 
        { BuiltSoundEntry( "fandangos", fandangos_pattern, IDC_RADIO6 ) }
    };
    RYTHMS_t rythms[sizeof( all_rythms ) / sizeof( * all_rythms )];

    for ( i = 0; i < sizeof( all_rythms ) / sizeof( * all_rythms ); i++ )
    {
        strcpy( rythms[i].name, all_rythms[i].name );
        rythms[i].idc_dialog = all_rythms[i].idc_dialogs;
        ContainerInit
        ( 
            & rythms[i].ctai_patterns, 
            all_rythms[i].pattern, 
            all_rythms[i].size, 
            sizeof( * all_rythms[i].pattern ), 
            TRUE 
        );
    }

    ContainerInit( & ctai_rythms, rythms, sizeof( rythms ), sizeof( * rythms ), TRUE );
}

int totalk = 0;

CONTAINER_t * CheckRadioButtons( HWND hDlg )
{
    HWND hwnd;
    BOOL last = FALSE;
    RYTHMS_t * now_rythms;

    ContainerResetIndex( & ctai_rythms );
    while ( 1 )
    {   
        now_rythms =  ((RYTHMS_t *) ContainerGetElement( & ctai_rythms ) );

        hwnd = GetDlgItem(hDlg, now_rythms->idc_dialog);
        if( SendMessage(hwnd, BM_GETCHECK, (WPARAM)0 ,(LPARAM)0) == BST_CHECKED )
            break;

        ContainerNextElement( & ctai_rythms, & last );
        if ( last )
            break;
    }

    if ( last )
        return NULL;

    return & now_rythms->ctai_patterns;
}

typedef struct EDIT_TABLE_s
{
    int i;
    CONTAINER_t * container;
} EDIT_TABLE_t;

CONTAINER_t edit_table;

BOOL CALLBACK AppDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    char bpm_str[100];
    char nr_compas_str[100];
    unsigned int bpm;
    unsigned int nr_compas;
    CONTAINER_t * pattern;
    int i;
    RYTHMS_t new_rythm;
    RYTHMS_t * aux_rythm;
    int pos;
    int j;
    HWND hwndList;
    int lbItem;
    EDIT_TABLE_t own_edit_table;
    BOOL last;


    memset( nr_compas_str, 0, sizeof( nr_compas_str ) );
    memset( bpm_str, 0, sizeof( bpm_str ) );

    switch( uMsg )
    {
        case WM_INITDIALOG:
            GetTheResource( "golpe", & golpe  );
            GetTheResource( "silencio", & silencio  );
            GetTheResource( "palma", & palma  );

            MakeBuiltSounds();
        
            SetClassLongPtr(hDlg, GCLP_HICON, (long)LoadIcon( GetModuleHandle( NULL ), MAKEINTRESOURCE(IDI_ICON1)));
            SetDlgItemTextA(hDlg,IDC_EDIT1,DEFAULT_BPM_STR);
            SetDlgItemTextA(hDlg,IDC_EDIT3,DEFAULT_COMPAS_STR);
            SendMessage(GetDlgItem(hDlg, IDC_RADIO1), BM_SETCHECK, TRUE, 0);
        
            return 1;

        break;

        case WM_COMMAND:
            switch(wParam)
            {
                case IDC_BUTTON1:
                    ShellExecute(NULL, "open", "http://wildflamenco.blogspot.com/", NULL, NULL, SW_SHOWNORMAL);
                break;
            
                case IDSTARTT:
                    StartStopTimer( FALSE, NULL, 0 );
                
                    if ( GetWindowTextA( GetDlgItem(hDlg, IDC_EDIT1), bpm_str, sizeof(bpm_str) - 1) == 0 )
                        FixBPM( hDlg, & bpm );
                    else
                    {
                        bpm = atoi( bpm_str );
                        if ( bpm >= MAX_BPM_ALLOWED )
                            FixBPM( hDlg, & bpm );
                    }
                
                    pattern = CheckRadioButtons( hDlg );
                    if ( pattern != NULL )
                        StartStopTimer( TRUE, pattern, bpm );
                    else
                    {
                        hwndList = GetDlgItem(hDlg, IDC_RADIO9); 
                        lbItem = (int)SendMessage(hwndList, LB_GETCURSEL, 0, 0); 
                        i = (int)SendMessage(hwndList, LB_GETITEMDATA, lbItem, 0);

                        ContainerResetIndex( & edit_table );
            
                        while ( 1 )
                        {
                            if ( ( (EDIT_TABLE_t * ) ContainerGetElement( & edit_table ))->i == i )
                                break;
                            ContainerNextElement( & edit_table, NULL );
                        }
                   
                        StartStopTimer( TRUE, ( (EDIT_TABLE_t * ) ContainerGetElement( & edit_table ))->container, bpm );
                    }
                break;
            
                case IDC_BUTTON2:
                    StartStopTimer( FALSE, NULL, 0 );
                
                    GetWindowTextA( GetDlgItem(hDlg, IDC_EDIT3), nr_compas_str, sizeof(nr_compas_str) - 1);
                    nr_compas = atoi( nr_compas_str );
                    
                    if ( nr_compas > MAX_COMPAS_ALLOWED )
                    {
                        MessageBoxA( hDlg, "Error: TOO COMPAS - MUCHOS COMPASES", "Error: TOO COMPAS - MUCHOS COMPASES", MB_OK );
                        SetDlgItemTextA(hDlg,IDC_EDIT3,DEFAULT_COMPAS_STR);
                    }
                    else if ( nr_compas == 0 )
                    {
                        MessageBoxA( hDlg, "Error: IT IS NECESSARY 1 COMPAS MIN", "Error: IT IS NECESSARY 1 COMPAS MIN", MB_OK );
                        SetDlgItemTextA(hDlg,IDC_EDIT3,DEFAULT_COMPAS_STR);
                    }
                    else
                    {      
                        dialog2_args.cancel = FALSE;
                        dialog2_args.total = nr_compas;
                        for ( i = 0; i < nr_compas; i++ )
                        {
                            dialog2_args.now = i;

                            memset( & dialog2_args.pattern, 0, sizeof( dialog2_args.pattern ) );
                            
                            DialogBoxParam( NULL, MAKEINTRESOURCE( IDD_DIALOG2 ), hDlg, AppDlgProc2, 0 );
                            if ( dialog2_args.cancel )
                                break;
                        }

                        if ( dialog2_args.cancel == FALSE )
                        {
                            itoa( ContainerGetNrElement( & ctai_rythms ) + 1, new_rythm.name, 10 );

                            new_rythm.idc_dialog = 0;

                            memcpy( & new_rythm.ctai_patterns, & dialog2_args.pattern, sizeof( dialog2_args.pattern ) );

                            ContainerAddElement( & ctai_rythms, & new_rythm );

                            pos = (int)SendMessage(GetDlgItem(hDlg, IDC_LIST1), LB_ADDSTRING, 0, (LPARAM) new_rythm.name ); 
                            SendMessage(GetDlgItem(hDlg, IDC_LIST1), LB_SETCURSEL, pos, 0); 
                            SendMessage(GetDlgItem(hDlg, IDC_LIST1), LB_SETITEMDATA, pos, (LPARAM) totalk++); 

                            ContainerResetIndex( & ctai_rythms );
                            while ( 1 ) 
                            {
                                aux_rythm = ContainerGetElement( & ctai_rythms );
                                ContainerNextElement( & ctai_rythms, & last );
                                if ( last )
                                    break;
                            }
                            own_edit_table.container = & aux_rythm->ctai_patterns;
                            own_edit_table.i = totalk - 1;
                            if ( ContainerGetNrElement( & edit_table ) == 0 )
                            {
                                ContainerInit( & edit_table, & own_edit_table, sizeof( own_edit_table ), sizeof( own_edit_table ), TRUE );
                            }
                        }
                    }
                break;
            
                case IDSTOPP:
                    StartStopTimer( FALSE, NULL, 0 );
                break;
            
                case IDCANCEL:
                    StartStopTimer( FALSE, NULL, 0 );

                    srand(time(NULL));
                    int r = rand() % 100;
                    // I dont want a spam overflow x)
                    if ( r < 3 )
                        ShellExecute(NULL, "open", "http://wildflamenco.blogspot.com/", NULL, NULL, SW_SHOWNORMAL);

                  EndDialog(hDlg, 0);
                break;
            }
        break;
    }

    return 0;
}


int WINAPI WinMain (HINSTANCE hThisInstance,
                    HINSTANCE hPrevInstance,
                    LPSTR lpszArgument,
                    int nFunsterStil)

{
    DialogBoxParam( hThisInstance, MAKEINTRESOURCE( IDD_DIALOG1 ), 0, AppDlgProc, 0 );

    return 0;
}
